<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>4.3.1-设计一个完善的响应系统</title>
</head>

<body>
    <div id="container"></div>
</body>
<script>
    /**
     * 尝试构造一个更加完善的响应系统
     * 
     * 一个响应系统的工作流程如下
     * - 当读取操作发生时，将副作用函数收集到“桶”中
     * - 当设置操作发生时，从“桶”中取出副作用函数并执行
     * 
     * 希望的是，哪怕副作用函数是一个匿名函数，也能够被正确地收集到“桶”中
     * - 提供一个用来注册副作用函数的机制
     * 
     * 在响应式数据 obj 上设置一个不存在的属性时
     * - 理论上，字段 obj.notExist 并没有与副作用建立响应联系，
     *    因此，定时器内语句的执行不应该触发匿名副作用函数重新执行
     * - 但如果我们执行上述这段代码就会发现，定时器到时后，
     *    匿名副作用函数却重新执行了，这是不正确的
     * 
     * */ 

    // 存储副作用函数的桶
    const bucket = new Set()
    // 原始数据
    const data = { text: 'hello world' }
    // 对原始数据的代理
    const obj = new Proxy(data, {
        get(target, key) {
            // 将 activeEffect 中存储的副作用函数收集到“桶”中
            if (activeEffect) {  // 新增 判断
                bucket.add(activeEffect) 
            } 
            return target[key]
        },
        set(target, key, newVal) {
            target[key] = newVal
            bucket.forEach(fn => fn())
            return true
        }
    })
    
    // 用一个全局变量存储被注册的副作用函数
    let activeEffect // 新增
    // effect 函数用于注册副作用函数
    function effect(fn) { // 新增 effect
        // 当调用 effect 注册副作用函数时，将副作用函数 fn 赋值给 activeEffect
        activeEffect = fn
        // 执行副作用函数
        fn()
    }

    // 如下所示的方式使用 effect 函数
    effect(
        // 一个匿名的副作用函数
        () => {
            // 会触发响应式数据 obj.text 的读取操作
            // 进而触发代理对象 Proxy 的 get 拦截函数：
            console.log('effect run') // 会打印 2 次 !!
            document.body.innerText = obj.text
        }
    )

    setTimeout(() => {
        // 副作用函数中并没有读取 notExist 属性的值
        obj.notExist = 'hello vue3'

        console.log('obj.notExist', obj.notExist)
    }, 1000)
</script>

</html>